package org.liveboardgames.agricola.business;

import java.util.ArrayList;
import java.util.List;

import org.liveboardgames.agricola.access.IGameDao;
import org.liveboardgames.agricola.business.helper.GameHelper;
import org.liveboardgames.agricola.business.updater.GameUpdater;
import org.liveboardgames.agricola.business.updater.PlayerUpdater;
import org.liveboardgames.agricola.controller.AgricolaUpdateAdapter;
import org.liveboardgames.agricola.domain.Change;
import org.liveboardgames.agricola.domain.Game;
import org.liveboardgames.agricola.domain.action.Action;
import org.liveboardgames.agricola.domain.player.Player;
import org.liveboardgames.agricola.domain.player.Score;
import org.liveboardgames.agricola.model.enums.ChangesEnum;

public class RoundServiceImpl implements IRoundService {

  private IScoreService scoreService;

  private IGameDao gameDao;

  public void setGameDao(IGameDao gameDao) {
    this.gameDao = gameDao;
  }

  public void setScoreService(IScoreService scoreService) {
    this.scoreService = scoreService;
  }

  public void startRound(Long gameId) {
    List<Change> changes = new ArrayList<Change>();

    // Retrieve the game that needs to be updated
    Game game = gameDao.load(gameId);

    startRound(game, changes);
  }

  private void startRound(Game game, List<Change> changes) {
    GameUpdater updater = new GameUpdater(game, changes);

    // Check if the game is finished
    GameHelper gameHelper = new GameHelper();
    boolean isGameFinished = gameHelper.isGameFinished(game);
    
    // Make sure all peeps are now adults
    updater.growUpPeeps();
    
    // Allow the first player to start their turn
    updater.setCurrentPlayer(game.getPlayers().get(0));
    updater.setHasPlayerPlayedPeep(false);

    // If the game is still ongoing, we start the new round
    if (!isGameFinished) {

      // Return all the peeps to their farm
      // updater.sendAllPeepsHome();

      // Add the next action card
      updater.addNextActionCard();

      // Execute all the start of round actions
      // Retrieve all the actions that trigger at the start of the round
      List<Action> startOfRoundActions = new GameHelper().getAllStartOfRoundActions(game);

      // Execute each action
      IActionService actionService = new ActionServiceImpl();
      for (Action action : startOfRoundActions) {
        List<Change> actionChanges = actionService.executeAction(game, action, null);
        changes.addAll(actionChanges);
      }

      gameDao.save(game);
      new AgricolaUpdateAdapter().publishGameUpdates(game.getId(), changes);
    }
    // If it is finished, we send back the score
    else {
      finishGame(game, changes);
    }
  }

  public void endTurn(Long gameId) {
    List<Change> changes = new ArrayList<Change>();

    // Retrieve the game that needs to be updated
    Game game = gameDao.load(gameId);

    // Get the next player to play
    Player nextPlayer = new GameHelper().findNextPlayerToPlay(game);

    // If no one can play, the round is finished
    if (nextPlayer == null) {
      finishRound(game, changes);
    }
    // Else, initialize the next player
    else {
      GameUpdater updater = new GameUpdater(game, changes);
      updater.setHasPlayerPlayedPeep(false);
      updater.setCurrentPlayer(nextPlayer);
    }

    gameDao.save(game);

    new AgricolaUpdateAdapter().publishGameUpdates(gameId, changes);
  }

  private void finishRound(Game game, List<Change> changes) {
    GameUpdater updater = new GameUpdater(game, changes);

    // Return all the peeps to their farm
    updater.sendAllPeepsHome();

    // Define the next player (either to play or to harvest)
    updater.setCurrentPlayer(game.getPlayers().get(0));

    // Harvest is needed
    boolean isHarvest = new GameHelper().isHarvest(game);
    if (isHarvest) {
      Change change = new Change(ChangesEnum.HARVEST_START, null);
      changes.add(change);
    }
    else {
      startRound(game, changes);
    }
  }

  private void finishGame(Game game, List<Change> changes) {

    List<Player> players = game.getPlayers();
    for (Player player : players) {
      PlayerUpdater playerUpdater = new PlayerUpdater(player, changes);
      Score score = scoreService.computeScore(player);
      playerUpdater.updateScore(score);
    }

    // Add the information that the game is finished
    new GameUpdater(game, changes).updateEndGame();

    // Save the changes
    gameDao.save(game);
    new AgricolaUpdateAdapter().publishGameUpdates(game.getId(), changes);
  }
}
